/*
Typecast 1.4 (release)
by Ara Pehlivanian (http://arapehlivanian.com)

This work is licensed under a Creative Commons Licence
http://creativecommons.org/licenses/by-nd/2.5/
*/

var Typecast = {
	InitMask : false,
	InitSuggest : false,
	
	Init : function(){
		this.Parse(document.body.getElementsByTagName("input"));
		this.Behaviours.Mask.Init();
		this.Behaviours.Suggest.Init();
	},
	
	Parse : function(nodes){
		
		for(var i=0; i<nodes.length; i++){
			var node = nodes[i];

			if(node.type=="text" && node.className && node.className.indexOf("TC") != -1){
				if(!node.id) Typecast.Utils.GenerateID(node);
				
				var behaviourName = (node.className.indexOf("[") != -1) ? node.className.substring(node.className.indexOf("TC")+2, node.className.indexOf("[")) : node.className.substring(node.className.indexOf("TC")+2, node.className.length);
				
				Typecast["Init" + behaviourName] = true;
				Typecast.Behaviours[behaviourName].InitField(node);

				node.onfocus = Typecast.Behaviours[behaviourName].Run;
				node.onkeyup = Typecast.Behaviours[behaviourName].KeyHandler;
				node.onkeydown = Typecast.Behaviours[behaviourName].KeyHandler;
				node.onblur = Typecast.Behaviours[behaviourName].Stop;
				node.onmouseup = Typecast.Behaviours[behaviourName].MouseUp;
			}
		}
	},
	
	Behaviours : {
		Mask : {
			Init : function(){
			},
			
			InitField : function(field){
				var fieldData = [];
				if(!eval("Typecast.Config.Data.Mask.Masks." + field.id)){
					fieldData = field.className.substring(field.className.indexOf("[")+1, field.className.indexOf("]"))
				}else{
					fieldData = eval("Typecast.Config.Data.Mask.Masks." + field.id);
				}
				Typecast.Behaviours.Mask.ParseFieldData(field, fieldData);
				field.value = field.DefaultText.join("");
			},
			
			Run : function(e){
				e = (!e) ? window.event : e;
			},
			
			Stop : function(){
			},
			
			KeyHandler : function(e){
				e = (!e) ? window.event : e;
				var mask = Typecast.Behaviours.Mask;
				
				mask.CursorManager.TabbedInSetPosition(this);
				
				//Backspace
				if(e.keyCode==8 && e.type=="keydown" && this.AllowInsert){
					var preBackspaceCursorPosition = Typecast.Behaviours.Mask.CursorManager.GetPosition(this)[0];
					mask.CursorManager.Move(this, -1);
					var postBackspaceCursorPosition = Typecast.Behaviours.Mask.CursorManager.GetPosition(this)[0];
					
					if(preBackspaceCursorPosition != postBackspaceCursorPosition) mask.DataManager.RemoveCharacterByShiftLeft(this);
					mask.Render(this);
				}
				
				//Tab
				if(e.keyCode==9 && e.type=="keydown"){
					return
				}
				
				//Enter
				else if(e.keyCode==13 && e.type=="keyup"){
					return
				}
				
				//Esc
				else if(e.keyCode==27 && e.type=="keyup"){
				}
				
				//End
				else if(e.keyCode==35 && e.type=="keydown"){
					var startIdx = Typecast.Behaviours.Mask.MaskManager.FindNearestMaskCharacter(this, this.DataIndex[this.DataIndex.length-1], 1);
					Typecast.Behaviours.Mask.CursorManager.SetPosition(this, startIdx);
				}

				//Home
				else if(e.keyCode==36 && e.type=="keydown"){
					Typecast.Behaviours.Mask.CursorManager.SetPosition(this, this.MaskIndex[0]);
				}
				
				//Left or Up
				else if(e.keyCode==37 && e.type=="keydown" || e.keyCode==38 && e.type=="keydown"){
					mask.CursorManager.Move(this, -1);
				}
				
				//Right or Down
				else if(e.keyCode==39 && e.type=="keydown" || e.keyCode==40 && e.type=="keydown"){
					mask.CursorManager.Move(this, 1);
				}
				
				//Insert
				else if(e.keyCode==45 && e.type=="keydown" && this.AllowInsert){
					mask.CursorManager.ToggleInsert(this);
				}
				
				//Delete
				else if(e.keyCode==46 && e.type=="keydown"){
					if(this.InsertActive){
						mask.DataManager.RemoveCharacterByShiftLeft(this);
					}else{
						mask.DataManager.RemoveCharacterByOverwrite(this);
					}
					mask.Render(this);
				}
				
				//Numeric Characters
				else if((mask.MaskManager.CurrentMaskCharacter(this) == Typecast.Config.Settings.Mask.MaskCharacters.Numeric) && (e.keyCode>=48 && e.keyCode<=57 && e.type=="keydown" || e.keyCode>=96 && e.keyCode<=105 && e.type=="keydown")){
					var keycode = parseInt(e.keyCode);
					keycode = (keycode>=96 && keycode<=105) ? keycode-48 : keycode;

					mask.DataManager.AddData(this, String.fromCharCode(keycode));
					mask.Render(this);
					mask.CursorManager.Move(this, 1);
				}
				
				//Alpha Characters 65 - 90
				else if((mask.MaskManager.CurrentMaskCharacter(this) == Typecast.Config.Settings.Mask.MaskCharacters.Alpha) && (e.keyCode>=65 && e.keyCode<=90 && e.type=="keydown")){
					mask.DataManager.AddData(this, String.fromCharCode(e.keyCode));
					mask.Render(this);
					mask.CursorManager.Move(this, 1);
				}
				
				//Refresh
				else if(e.keyCode==116 && e.type=="keydown"){
					return
				}
				
				else{
				}
				return false
			},
			
			MouseUp : function(e){
				e = (!e) ? window.event : e;
				var cursorPosition = Typecast.Behaviours.Mask.CursorManager.GetPosition(this)[0];
				var startIdx = Typecast.Behaviours.Mask.MaskManager.FindNearestMaskCharacter(this, cursorPosition, 0);
				Typecast.Behaviours.Mask.CursorManager.SetPosition(this, startIdx);
			},
			
			ParseFieldData : function(field, fieldData){
				fieldData = fieldData.split(Typecast.Config.Settings.Mask.FieldDataSeparator);
				field.Data = [];
				field.DataIndex = [];
				field.DefaultText = (fieldData[1]) ? fieldData[1].split("") : fieldData[0].split(""); //if default text isn't provided use mask
				field.Mask = this.MaskManager.ParseMask(fieldData[0]);
				field.MaskIndex = this.MaskManager.ParseMaskIndex(field.Mask);
				field.CursorPersistance = [];
				field.InsertActive = (this.MaskManager.IsComplexMask(field)) ? false : true;
				field.HighlightChar = (this.MaskManager.IsComplexMask(field)) ? true : false;
				field.AllowInsert = (this.MaskManager.IsComplexMask(field)) ? false : true;
			},
			
			MaskManager : {
				ParseMask : function(mask){
					var arr = [];
					var maskCharacters = Typecast.Config.Settings.Mask.MaskCharacters;
					for(var i=0; i<mask.length; i++){
						for(maskCharacter in maskCharacters){
							char = eval("maskCharacters." + maskCharacter);
							if(mask.substring(i, i+1) == char){
								arr[i] = char;
							}
						}
					}
					return arr
				},
				
				ParseMaskIndex : function(mask){
					var arr = [];
					for(var i=0; i<mask.length; i++){
						if(mask[i] != null) arr[arr.length] = i;
					}
					return arr;
				},
				
				CurrentMaskCharacter : function(field){
					var cursorPosition = Typecast.Behaviours.Mask.CursorManager.GetPosition(field)[0];
					return field.Mask[cursorPosition];
				},
				
				FindNearestMaskCharacter : function(field, cursorPosition, dir){
					var nearestMaskCharacter = (field.DataIndex.length > 0) ? cursorPosition : field.MaskIndex[0];
					
					switch(dir){
						case -1:
							for(var i=field.DataIndex.length-1; i>-1; i--){
								if(field.DataIndex[i] < cursorPosition){
									nearestMaskCharacter = field.DataIndex[i];
									break;
								}
							}
						break;
						case 0:
							for(var i=0; i<field.DataIndex.length; i++){
								if(field.MaskIndex[i] >= cursorPosition){
									nearestMaskCharacter = field.MaskIndex[i];
									break;
								}else{
									nearestMaskCharacter = field.MaskIndex[field.DataIndex.length];
								}
							}							
						break;
						case 1:
							for(var i=0; i<field.DataIndex.length; i++){
								if(field.DataIndex[i] > cursorPosition){
									nearestMaskCharacter = field.DataIndex[i];
									break;
								}
							}
							if(cursorPosition == field.MaskIndex[field.MaskIndex.length-1]) nearestMaskCharacter = cursorPosition + 1;
							else if(cursorPosition == field.DataIndex[field.DataIndex.length-1]) nearestMaskCharacter = field.MaskIndex[field.DataIndex.length];
						break;
					}
					return nearestMaskCharacter
				},
				
				IsComplexMask : function(field){//reports if mask contains mixed mask characters... can't perform "insert" if true
					var isComplex = false;
					var previousMaskChar = "";
					for(var i=0; i<field.MaskIndex.length; i++){
						var currentMaskChar = field.Mask[field.MaskIndex[i]];
						if(currentMaskChar != previousMaskChar && previousMaskChar != ""){
							isComplex = true;
						}
						previousMaskChar = currentMaskChar;
					}
					return isComplex
				}
			},
			
			CursorManager : {
				Move : function(field, dir){
					var cursorPosition = this.GetPosition(field)[0];
					var startIdx = Typecast.Behaviours.Mask.MaskManager.FindNearestMaskCharacter(field, cursorPosition, dir);
					this.SetPosition(field, startIdx);
				},
				GetPosition : function(field){
					var arr = [0,0];
					if(field.selectionStart && field.selectionEnd){
						arr[0] = field.selectionStart;
						arr[1] = field.selectionEnd;
					}
					else if(document.selection){
						var range = field.createTextRange();
						range.setEndPoint("EndToStart", document.selection.createRange());
						arr[0] = range.text.length;
						arr[1] = document.selection.createRange().text.length;
					}
					return arr
				},
				SetPosition : function(field, startIdx){
					var endIdx = startIdx + ((field.HighlightChar) ? 1 : 0);
					Typecast.Utils.PartialSelect(field, startIdx, endIdx);
				},
				TabbedInSetPosition : function(field){
					var mask = Typecast.Behaviours.Mask;
					
					if(mask.MaskManager.CurrentMaskCharacter(field) == undefined){
						var startIdx = null;
						if(field.DataIndex.length > 0 && field.DataIndex.length != field.MaskIndex.length){
							startIdx = field.MaskIndex[field.DataIndex.length];
						}
						else if(field.DataIndex.length == field.MaskIndex.length){
							startIdx = field.DataIndex[field.DataIndex.length-1] + 1;
						}
						else{
							startIdx = field.MaskIndex[0];
						}
						this.SetPosition(field, startIdx);
					}
				},
				PersistPosition : function(field){
					field.CursorPersistance = this.GetPosition(field);
				},
				RestorePosition : function(field){
					this.SetPosition(field, field.CursorPersistance[0]);
				},
				ToggleInsert : function(field){
					if(field.InsertActive){
						field.InsertActive = false;
						field.HighlightChar = true;
					}else{
						field.InsertActive = true;
						field.HighlightChar = false;
					}
					var startIdx = this.GetPosition(field)[0];
					this.SetPosition(field, startIdx);
				}
			},
			
			DataManager : {
				AddData : function(field, char){
					var cursorPosition = Typecast.Behaviours.Mask.CursorManager.GetPosition(field)[0];
					if(field.InsertActive){
						this.InsertCharacter(field, char);
					}else{
						this.OverwriteCharacter(field, char, cursorPosition);
					}
					this.UpdateDataIndex(field);
				},
				InsertCharacter : function(field, char){
					var lastCharacterPosition = field.MaskIndex[field.MaskIndex.length-1];
					var currentCharacterPosition = this.CurrentDataIndexPosition(field);
					for(var i=lastCharacterPosition; i>=currentCharacterPosition; i--){
						field.Data[field.MaskIndex[i+1]] = field.Data[field.MaskIndex[i]];
					}
					field.Data[field.MaskIndex[currentCharacterPosition]] = char;
				},
				OverwriteCharacter : function(field, char, cursorPosition){
					field.Data[cursorPosition] = char;
				},
				RemoveCharacterByOverwrite : function(field){
					var currentCharacterPosition = this.CurrentDataIndexPosition(field);
					if(currentCharacterPosition != null){
						field.Data[field.DataIndex[currentCharacterPosition]] = "";
					}
				},
				RemoveCharacterByShiftLeft : function(field){
					var lastCharacterPosition = field.DataIndex[field.DataIndex.length-1];
					var currentCharacterPosition = this.CurrentDataIndexPosition(field);
					var cursorPosition = Typecast.Behaviours.Mask.CursorManager.GetPosition(field)[0];
					
					if(currentCharacterPosition != null && lastCharacterPosition >= cursorPosition){
						for(var i=currentCharacterPosition; i<=lastCharacterPosition; i++){
							field.Data[field.DataIndex[i]] = field.Data[field.DataIndex[i+1]];
						}
						field.Data.length = field.Data.length-1;
						this.UpdateDataIndex(field);
					}
				},
				UpdateDataIndex : function(field){
					field.DataIndex.length = 0;
					for(var i=0; i<field.Data.length; i++){
						if(field.Data[i] != undefined) field.DataIndex[field.DataIndex.length] = i;
					}
				},
				CurrentDataIndexPosition : function(field){
					var cursorPosition = Typecast.Behaviours.Mask.CursorManager.GetPosition(field)[0];
					var currentDataIndexPosition = null;
					for(var i=0; i<field.MaskIndex.length; i++){
						if(field.MaskIndex[i] == cursorPosition){
							currentDataIndexPosition = i;
							break;
						}
					}
					return currentDataIndexPosition
				}				
			},
			
			Render : function(field){
				this.CursorManager.PersistPosition(field);
				var composite = [];
				for(var i=0; i<field.Mask.length; i++){
					composite[i] = field.Mask[i];
					if(field.DefaultText[i]) composite[i] = field.DefaultText[i];
					if(field.Data[i]) composite[i] = field.Data[i];
				}
				field.value = composite.join("")
				
				this.CursorManager.RestorePosition(field);
			}
		},
		
		Suggest : {
			FieldID : null,
			OutputAreaVisible : false,
			InputValueBackup : null,
			ResultSet : [],
			ResultSetIndex : -1,
			
			Init: function(){
				this.CreateOutputArea();
			},
			
			InitField : function(field){
			},
			
			Run : function(e){
				e = (!e) ? window.event : e;
				var suggest = Typecast.Behaviours.Suggest;
				this.autocomplete = (Typecast.Config.Settings.Suggest.BrowserAutoComplete) ? "on" : "off"; //Reference: http://web.archive.org/web/20031203134351/http://devedge.netscape.com/viewsource/2003/form-autocompletion/
				suggest.FieldID = this.id;
			},
			
			Stop : function(e){
				e = (!e) ? window.event : e;
				var suggest = Typecast.Behaviours.Suggest;
				
				suggest.HideOutputArea();
				suggest.InputValueBackup = null;
				suggest.ResultSet.length = 0;
				suggest.ClearOutputAreaContents();
			},
			
			KeyHandler : function(e){
				e = (!e) ? window.event : e;
				var suggest = Typecast.Behaviours.Suggest;
				
				//Tab
				if(e.keyCode==9 && e.type=="keyup"){
					return
				}
				
				//Enter
				else if(e.keyCode==13 && e.type=="keyup"){
					suggest.InputValueBackup = this.value;
					suggest.HideOutputArea();
					suggest.PartialSelect(this, this.value.length);
					return
				}
				
				//Shift
				else if(e.keyCode==16 && e.type=="keyup"){
					return false
				}
				
				//Esc
				else if(e.keyCode==27 && e.type=="keyup"){
					suggest.RevertInputValue(this);
					suggest.HideOutputArea();
					return
				}
				
				//Up
				else if(e.keyCode==38 && e.type=="keydown"){
					if(suggest.ResultSet.length==0 && this.value.length > 0){
						suggest.LookUp(this);
						suggest.Render(this);
						suggest.InputValueBackup = this.value;
					}
					suggest.MoveResultSetIndex(-1);
					suggest.SuggestInputValue(this);
					//return false
				}
				
				//Down
				else if(e.keyCode==40 && e.type=="keydown"){
					if(suggest.ResultSet.length==0 && this.value.length > 0){
						suggest.LookUp(this);
						suggest.Render(this);
						suggest.InputValueBackup = this.value;
					}
					suggest.MoveResultSetIndex(1);
					suggest.SuggestInputValue(this);
					//return false
				}
				
				//Everything else
				else if(e.type=="keyup" && e.keyCode!=37 && e.keyCode!=38 && e.keyCode!=39 && e.keyCode!=40){
					suggest.LookUp(this);
					suggest.Render(this);
					suggest.InputValueBackup = this.value;
					if(e.keyCode!=8){
						suggest.AutoComplete(this);
					}
				}
			},
			
			MouseUp : function(e){
				e = (!e) ? window.event : e;
				return
			},
			
			LookUp : function(field){
				field = (!field) ? document.getElementById(this.FieldID) : field;
				var dictionaries = Typecast.Config.Data.Suggest.Dictionaries;
				var dictionary = (eval("dictionaries." + field.id)) ? eval("dictionaries." + field.id) : dictionaries.Default;
				dictionary.sort();
				var query = Typecast.Utils.CaseSensitize(field.value);

				this.ResultSet.length = 0;
				
				for(var i=0; i<dictionary.length; i++){
					if(Typecast.Utils.FindIn(dictionary[i], query) > -1){
						this.ResultSet[this.ResultSet.length] = dictionary[i];
					}
				}
			},
			
			Render : function(field){
				field = (!field) ? document.getElementById(this.FieldID) : field;
				if (this.ResultSet.length == 0) {
					this.HideOutputArea();
					return
				}
				
				var outputArea = document.getElementById(Typecast.Config.Settings.Suggest.OutputAreaID);
				this.ClearOutputAreaContents();
				
				var ul = document.createElement("ul");
					ul.id = "Suggestions";

				for(var i=0; i<this.ResultSet.length && i<Typecast.Config.Settings.Suggest.ResultLimit; i++){
					var li = document.createElement("li");
					li.id = i;
					li.onmouseover = this.MouseOver;
					li.onmouseout = this.MouseOut;
					
					var result = this.BuildHighlightedResult(this.ResultSet[i], field.value);
					
					li.appendChild(result);
					ul.appendChild(li);
				}
				
				outputArea.appendChild(ul);
				this.ShowOutputArea(field);
			},
			
			SuggestInputValue : function(field){
				if(this.ResultSetIndex == -1) return
				field = (!field) ? document.getElementById(this.FieldID) : field;
				
				if(this.ResultSetIndex>=0){	//arrow
					field.value = this.ResultSet[this.ResultSetIndex];
				}else{ //typing
					field.value += this.ResultSet[this.ResultSetIndex].substring(field.value.length, this.ResultSet[this.ResultSetIndex].length);
				}
			},
			
			AutoComplete : function(field){
				if(!Typecast.Config.Settings.Suggest.MatchFromStart) return;
				field = (!field) ? document.getElementById(this.FieldID) : field;
				var startIdx = field.value.length;
				
				if(this.ResultSet.length > 0){
					field.value += this.ResultSet[0].substring(field.value.length, this.ResultSet[0].length);
					this.PartialSelect(field, startIdx);
				}
			},
			
			PartialSelect : function(field, startIdx){
				if(!Typecast.Config.Settings.Suggest.MatchFromStart) return;
				field = (!field) ? document.getElementById(this.FieldID) : field;

				Typecast.Utils.PartialSelect(field, startIdx, field.value.length);
			},
			
			RevertInputValue : function(field){
				field = (!field) ? document.getElementById(this.FieldID) : field;
				field.value = (field.value == this.InputValueBackup) ? "" : this.InputValueBackup;
			},
			
			MoveResultSetIndex : function(dir){
				if(!this.OutputAreaVisible) return;
				if(this.ResultSet.length == 0) return;
				if(this.ResultSetIndex == -1 && dir == -1) return //Lower Bound Test
				if(this.ResultSetIndex == this.ResultSet.length-1 && dir == 1) return //Upper Bound Test
				if(this.ResultSetIndex == Typecast.Config.Settings.Suggest.ResultLimit-1 && dir == 1) return //User Defined Upper Bound Test
				
				var outputArea = document.getElementById(Typecast.Config.Settings.Suggest.OutputAreaID);
				
				if(outputArea && this.ResultSetIndex != -1) outputArea.childNodes[0].childNodes[this.ResultSetIndex].className = "";
				
				this.ResultSetIndex += dir;
				
				if(outputArea && this.ResultSetIndex == -1) this.RevertInputValue();
				if(outputArea && this.ResultSetIndex != -1) outputArea.childNodes[0].childNodes[this.ResultSetIndex].className = "selected";
			},
			
			MouseOver : function(e){
				e = (!e) ? window.event : e;
				this.className = "selected";
				Typecast.Behaviours.Suggest.ResultSetIndex = parseInt(this.id);
				Typecast.Behaviours.Suggest.SuggestInputValue();
			},
			
			MouseOut : function(e){
				e = (!e) ? window.event : e;
				this.className = "";
			},
			
			CreateOutputArea : function(){
				var sid = Typecast.Config.Settings.Suggest.OutputAreaID;
				var outputAreaExists = (document.getElementById(sid)) ? true : false;
				
				if(!outputAreaExists){
					var outputArea = document.createElement("div");
					outputArea.id = sid;
				}else{
					var outputArea = document.getElementById(sid);
				}
				outputArea.style.display = "none";
				outputArea.style.position = "absolute";
				this.OutputAreaVisible = false;
				document.body.appendChild(outputArea);
			},
			
			ShowOutputArea : function(field){
				field = (!field) ? document.getElementById(this.FieldID) : field;
				if(this.OutputAreaVisible) return
				if(field.value == "") return
				
				var outputArea = document.getElementById(Typecast.Config.Settings.Suggest.OutputAreaID);
				var xy = Typecast.Utils.GetXY(field);
				
				if(outputArea){
					if(Typecast.Config.Settings.Suggest.IEForceRelative && document.all) outputArea.parentNode.style.position = "relative";
					outputArea.style.display = "block";
					outputArea.style.left = xy[0] + "px";
					outputArea.style.top = xy[1] + field.offsetHeight + "px";
					outputArea.style.width = field.offsetWidth + "px";
				}else{
					Typecast.Behaviours.Suggest.LookUp(this);
					Typecast.Behaviours.Suggest.Render(this);
				}
				this.OutputAreaVisible = true;
			},
			
			HideOutputArea : function(){
				if(!this.OutputAreaVisible) return
				this.ResultSetIndex = -1;
				document.getElementById(Typecast.Config.Settings.Suggest.OutputAreaID).style.display = "none";
				this.OutputAreaVisible = false;
			},
			
			ClearOutputAreaContents : function(){
				var sid = Typecast.Config.Settings.Suggest.OutputAreaID;
				var outputArea = document.getElementById(sid);
				
				if(outputArea && outputArea.childNodes.length > 0) outputArea.removeChild(outputArea.childNodes[0]);
			},
			
			BuildHighlightedResult : function(str, fragment){
				var csStr = Typecast.Utils.CaseSensitize(str);
				var csFragment = Typecast.Utils.CaseSensitize(fragment);
				var span = document.createElement("span");
				var lhs = document.createTextNode(str.substring(0, csStr.indexOf(csFragment)));
				var strong = document.createElement("strong");
				var highlight = document.createTextNode(str.substring(csStr.indexOf(csFragment), csStr.indexOf(csFragment) + csFragment.length));
				var rhs = document.createTextNode(str.substring(csStr.indexOf(csFragment) + csFragment.length, csStr.length));
				
				strong.appendChild(highlight);
				span.appendChild(lhs);
				span.appendChild(strong);
				span.appendChild(rhs);
				
				return span
			}
		}
	},
	
	Utils : {
		FindClass : function(findClass, parentClass){
			parentClass = (!parentClass) ? 'Typecast' : parentClass;
			for(var i in eval(parentClass)){
				if(i == findClass){
					return i;
				}else{
					var found = Typecast.Utils.FindClass(findClass, parentClass + "." + i);
					// if something was found send it back up the tree
					// if the parentClass contains no dots it's the rootClass and should also be tacked on and returned
					if(found) return (parentClass.indexOf(".") == -1) ? parentClass + "." + i + "." + found : i + "." + found;
				}
			}
		},
		
		GenerateID : function(obj){
			dt = new Date();
			obj.id = "GenID" + dt.getTime();
			return obj
		},
		
		FindIn : function(str, fragment){
			if(!fragment || fragment.length <= 0) return -1
			var csStrIdx = this.CaseSensitize(str).indexOf(fragment);
			var matchFromStart = Typecast.Config.Settings.Suggest.MatchFromStart;
			
			if(matchFromStart && csStrIdx==0 ) {return csStrIdx}
			else if(!matchFromStart && csStrIdx!=-1) {return csStrIdx}
			else {return -1}
		},
		
		CaseSensitize : function(str){
			var isCaseSensitive = Typecast.Config.Settings.Suggest.isCaseSensitive;
			return (isCaseSensitive) ? str : str.toLowerCase();
		},
		
		GetXY : function(obj){
			var x=0;
			var y=0;
			while(obj.offsetParent){
				x+=obj.offsetLeft;
				y+=obj.offsetTop;
				obj = obj.offsetParent;
			}
			return [x,y]
		},
		
		PartialSelect : function(field, startIdx, endIdx){
			if(field.createTextRange){
				var fld= field.createTextRange();
				fld.moveStart("character", startIdx);
				fld.moveEnd("character", endIdx - field.value.length);
				fld.select();
			}else if(field.setSelectionRange){
				field.setSelectionRange(startIdx, endIdx);
			}
		}		
	}
}